﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestTasks.Cataloguer
{
    public class DocumentComparer : IComparer<IDocument>
    {
        class SubComparer
        {
            public Type Type { get; set; }
            public string Property { get; set; }
            public Comparison<object> Comparer { get; set; }
            public bool IsDescending { get; set; }
        }

        readonly List<SubComparer> subComparers;

        public DocumentComparer() { subComparers = new List<SubComparer>(); }
        
        public int Compare(IDocument x, IDocument y)
        {
            Func<IDocument, string, Type, bool> hasValue = delegate(IDocument doc, string property, Type type) {
                if (!doc.HasSet(property)) return false;
                if (doc.Get(property)!=null && doc.Get(property).GetType()!=type) return false;
                return true;
            };

            foreach (var comparer in subComparers)
            {
                bool xf = hasValue(x, comparer.Property, comparer.Type), yf = hasValue(y, comparer.Property, comparer.Type);
                if (!xf && !yf) continue;
                if (!xf && yf) return -1;
                if (xf && !yf) return 1;
                var f = comparer.Comparer(x.Get(comparer.Property), y.Get(comparer.Property));
                switch (comparer.IsDescending ? -f : f)
                {
                    case -1: return -1;
                    case 1: return 1;
                    case 0: continue;
                    default: throw new Exception();
                }
            }

            return 0;
        }

        public void Reset() { subComparers.Clear(); }
        
        /// <summary>
        /// Modify current version
        /// </summary>
        public DocumentComparer OrderByDescending<T>(string property, Comparison<T> comparer) 
        {
            subComparers.Add(new SubComparer { 
                Comparer = (x,y) => comparer((T)x,(T)y),
                IsDescending = true,
                Property = property,
                Type = typeof(T)
            });
            return this;
        }

        /// <summary>
        /// Modify current version
        /// </summary>
        public DocumentComparer OrderBy<T>(string property, Comparison<T> comparer) 
        {
            subComparers.Add(new SubComparer
            {
                Comparer = (x, y) => comparer((T)x, (T)y),
                Property = property,
                Type = typeof(T)
            });
            return this;
        }
    }
}
